package com.significantfiles.snapshot.engine.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import com.significantfiles.snapshot.config.TransactionStorageConfig;
import com.significantfiles.snapshot.engine.tx.Transaction;
import com.significantfiles.snapshot.engine.tx.TransactionException;

public class TransactionStorage {

	private int txIndex = 0;

	private final int maxRange;

	private final File storageDir;

	public TransactionStorage(final TransactionStorageConfig conf) {
		this.maxRange = conf.getSnapshotRange();
		this.storageDir = conf.getDir();
	}

	public synchronized void storeTx(final Transaction<?> tx)
			throws TransactionException {
		final File txFileName = calcTxFileName(this.txIndex);

		storeTxAsFile(tx, txFileName);

		incTxIndex();
	}

	private int incTxIndex() {
		return this.txIndex = (this.txIndex + 1) % maxRange;
	}

	private void storeTxAsFile(final Transaction<?> tx, final File txFileName)
			throws TransactionException {
		ObjectOutputStream out = null;
		try {
			out = getOutputStream(txFileName);

			out.writeObject(tx);

			out.flush();
		} catch (final Throwable x) {
			x.printStackTrace();
			throw new TransactionException("Couldn't serialize Transaction.");
		} finally {
			closeStream(out);
		}
	}

	private void closeStream(ObjectOutputStream out) {
		if (out == null)
			return;

		try {
			out.close();
		} catch (final IOException e) {
			;
		}
	}

	private ObjectOutputStream getOutputStream(final File txFileName)
			throws IOException {
		final FileOutputStream storage = new FileOutputStream(txFileName);
		final BufferedOutputStream buffer = new BufferedOutputStream(storage,
				1024);
		return new ObjectOutputStream(buffer);
	}

	private File calcTxFileName(int idx) {
		final String fileName = ("tx-" + idx + ".ser");
		return new File(this.storageDir, fileName);
	}

	public Transaction<?> loadLastTransaction() throws IOException {
		final int idx = (this.txIndex - 1) % maxRange;
		final File txFile = calcTxFileName(idx);

		final FileInputStream storage = new FileInputStream(txFile);
		final BufferedInputStream buffer = new BufferedInputStream(storage,
				1024);
		final ObjectInputStream out = new ObjectInputStream(buffer);

		try {
			return (Transaction<?>) out.readObject();
		} catch (final Throwable e) {
			e.printStackTrace();

			throw new RuntimeException(e);
		}
	}

	public File getStorageDir() {
		return storageDir;
	}

	public boolean shouldCreateSnapshot() {
		return (txIndex == (maxRange - 1));
	}

}
